package server.util;


import javax.servlet.http.Cookie;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Map;

//提供参数解析，cookie解析等方法
public final class RequestUtil {
    //将请求头的value解析为Cookie数组
    public static Cookie[] parseCookie(String value) {
        if (value == null || value.length() < 1) {
            return new Cookie[0];
        }
        ArrayList<Cookie> cookies = new ArrayList<>();
        while (value.length() > 0) {
            int index = value.indexOf(';');
            if (index == 0) { //value最后有个; (不是规范格式)
                break;
            } else if (index < 0) {
                index = value.length();
            }
            String keyAndValue = value.substring(0, index);

            if (index < value.length())
                value = value.substring(index + 1);
            else
                value = "";
            try {
                int mid = keyAndValue.indexOf('=');
                if (mid > 0) {
                    String name = keyAndValue.substring(0, mid).trim();
                    String val = keyAndValue.substring(mid + 1).trim();
                    cookies.add(new Cookie(name, val));
                }
            } catch (Throwable e) {

            }
        }
        return cookies.toArray(new Cookie[cookies.size()]);
    }
    //给定字符串，解析出里面的参数对并放到map中
    public static void parseParameters(Map<String, String[]> map, String data, String encoding) throws UnsupportedEncodingException {
        if (data == null || data.length() <= 0)
            return;
        parseParameters(map, data.getBytes(), encoding);

    }
    //给定byte数组，解析出里面的参数对并放到map中
    public static void parseParameters(Map<String, String[]> map, byte[] data, String encoding) throws UnsupportedEncodingException {
        if (data == null || data.length <= 0)
            return;
        String key = null;
        String value = null;
        int index = 0;
        int strIndex = 0;
        while (index < data.length) {
            byte c = data[index++];
            switch ((char)c) {
                case '&':
                    //说明strIndex前面是value
                    value = new String(data, 0, strIndex, encoding);
                    if (key != null && key.length() > 0) {
                        putMapEntry(map, key, value);
                        key = null;
                    }
                    //重新开始记录下一个串
                    strIndex = 0;
                    break;
                case '=':
                    //说明strIndex前面是key
                    key = new String(data, 0, strIndex, encoding);
                    //重新开始记录下一个串
                    strIndex = 0;
                    break;
                case '+':
                    //+换成空格
                    //　W3C标准规定，当Content-Type为application/x-www-form-urlencoded时，URL中查询参数名和参数值中空格要用加号+替代，所以几乎所有使用该规范的浏览器在表单提交后，URL查询参数中空格都会被编成加号+。
                    // 而在另一份规范RFC2396，定义URI里, URI里的保留字符都需转义成%HH格式(Section 3.4 Query Component)，因此空格会被编码成%20，加号+本身也作为保留字而被编成%2B，对于某些遵循RFC 2396标准的应用来说，它可能不接受查询字符串中出现加号+，认为它是非法字符。
                    data[strIndex++] = ' ';
                    break;
                case '%':
                    byte high = convertHexDigit(data[index++]);
                    byte low = convertHexDigit(data[index++]);
                    data[strIndex++] = (byte)((high << 4) | low);
                    break;
                default:
                    data[strIndex++] = c;
            }

        }
        //最后一对参数
        if (key != null && key.length() > 0) {
            value = new String(data, 0, strIndex, encoding);
            putMapEntry(map, key, value);
        }
    }
    //向map中放键值对
    //之前有元素需要重造数组再放进去
    private static void putMapEntry(Map<String, String[]> map, String key, String value) {
        String[] newArray = null;
        String[] oldArray = map.get(key);
        if (oldArray == null) {
            newArray = new String[1];
            newArray[0] = value;
        } else {
            newArray = new String[oldArray.length + 1];
            System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
            newArray[newArray.length - 1] = value;
        }
        map.put(key, newArray);
    }

    //字节表示的十六进制字符转为数字 其他字符统一转成0
    private static byte convertHexDigit(byte b) {
        if (b >= '0' && b <= '9')
            return (byte)(b - '0');
        if (b >= 'a' && b <= 'f')
            return (byte)(b - 'a' + 10);
        if (b >= 'A' && b <= 'F')
            return (byte)(b - 'A' + 10);
        return 0;
    }
}
